`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date: 2021/04/27 19:43:30
// Design Name: 
// Module Name: tiled_conv
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////


module tiled_conv
#(
parameter Tr =14,
parameter Tc =14,
parameter K = 3,
parameter Tm = 4,
parameter Tn = 2)
(
input logic clk,
input logic rst,
input logic start,
output logic done,
output logic [15:0] InAddr,
output logic [15:0] KernelAddr,
output logic [15:0] ReadOutAddr,
output logic [15:0] WriteOutAddr,
input logic [15:0] InData[0:Tn-1],
input logic [15:0] KernelData[0:Tm-1][0:Tn-1],
input logic [15:0] ReadOut[0:Tm-1],
output logic [15:0] WriteOut[0:Tm-1],
output logic we
    );

logic [9:0]row_cnt;
logic [9:0]col_cnt;
logic [9:0]kx_cnt;
logic [9:0]ky_cnt;
logic busy;
logic busy_d1;
logic busy_d2;
logic busy_d3;
logic [15:0] TmpBuffer[0:Tm-1][0:Tn-1];
logic [15:0] ReadOut_d1[0:Tm-1];
logic [15:0] OutAddr_ff1;
logic [15:0] OutAddr_ff2;
logic done_ff0;
logic done_ff1;
logic done_ff2;
logic done_ff3;

assign done_ff0=(row_cnt==Tr-1&&col_cnt==Tc-1&&kx_cnt==K-1&&ky_cnt==K-1)?1:0;
always_ff@(posedge clk)
    {done,done_ff3,done_ff2,done_ff1}<={done_ff3,done_ff2,done_ff1,done_ff0};
//busy
always_ff@(posedge clk,posedge rst)
if(rst)
   busy<=0;
else if(start)
   busy<=1;
else if(done_ff0)
   busy<=0;
//busy_d1,d2,d3
always_ff@(posedge clk,posedge rst)
if(rst)
    {busy_d3,busy_d2,busy_d1}<=3'b000;
else 
    {busy_d3,busy_d2,busy_d1}<={busy_d2,busy_d1,busy};
//col_cnt
always_ff@(posedge clk,posedge rst)
if(rst)
     col_cnt<=0;
else if(busy)
    if(col_cnt==Tc-1)
        col_cnt<=0;
    else
        col_cnt<=col_cnt+1;
else if(done_ff0)
    col_cnt<=0;
//row_cnt
always_ff@(posedge clk,posedge rst)
if(rst)
    row_cnt<=0;
else if(busy&&col_cnt==Tc-1)
    if(row_cnt==Tr-1)
        row_cnt<=0;
    else
        row_cnt<=row_cnt+1;
else if(done_ff0)
    row_cnt<=0;
//ky_cnt
always_ff@(posedge clk,posedge rst)
if(rst)
    ky_cnt<=0;
else if(busy&&col_cnt==Tc-1&&row_cnt==Tr-1)
    if(ky_cnt==K-1)
        ky_cnt<=0;
    else
        ky_cnt<=ky_cnt+1;
else if(done_ff0)
    ky_cnt<=0;
//kx_cnt
always_ff@(posedge clk,posedge rst)
if(rst)
    kx_cnt<=0;
else if(busy&&ky_cnt==K-1&&col_cnt==Tc-1&&row_cnt==Tr-1)
    if(kx_cnt==K-1)
        kx_cnt<=0;
    else
        kx_cnt<=kx_cnt+1;
else if(done_ff0)
    kx_cnt<=0;
//InAddr
always_ff@(posedge clk,posedge rst)
if(rst)
    InAddr<=0;
else if(busy)
    InAddr<=(row_cnt+kx_cnt)*(Tc+K-1)+(col_cnt+ky_cnt);                    //
//KernelAddr
always_ff@(posedge clk,posedge rst)
if(rst)
   KernelAddr<=0;
else if(busy)
   KernelAddr<=kx_cnt*K+ky_cnt;
//ReadOutAddr
always_ff@(posedge clk,posedge rst)
if(rst)
    ReadOutAddr<=0;
else if(busy)
    ReadOutAddr<=row_cnt*Tc+col_cnt;
//mult
always_ff@(posedge clk)
begin
    for(int m=0;m<Tm;m++)
        for(int n=0;n<Tn;n++)
             TmpBuffer[m][n]<=KernelData[m][n]*InData[n];
end
//add
always_ff@(posedge clk)
begin
    for(int m=0;m<Tm;m++)
    begin
          WriteOut[m]<=ReadOut_d1[m]+TmpBuffer[m][0]+TmpBuffer[m][1]+TmpBuffer[m][2]+TmpBuffer[m][3];          //
    end
end
//we
always_ff@(posedge clk,posedge rst)
if(rst)
   we<=0;
else if(busy_d3)
   we<=1;
else
   we<=0;
//WriteAddr
always_ff@(posedge clk)
    {WriteOutAddr,OutAddr_ff2,OutAddr_ff1}<={OutAddr_ff2,OutAddr_ff1,ReadOutAddr};
//ReadOut_d1
always_ff@(posedge clk)
    ReadOut_d1<=ReadOut;
endmodule
